#include <GL/glew.h>
#include <GL/glut.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/freeglut.h>
#include <iostream>
#include <stdio.h>
#include <unistd.h>
#include <cmath>
#include <cstdlib>
#include <string.h>
#include <assert.h>
#include <math.h>
#include "CInitGL.h"
#include "math_3d.h"
#include "CShaders.h"
#include "CPipeline.h"
#include "Ccamera.h"
#include "CTexture.h"


#define WINDOW_WIDTH 1024
#define WINDOW_HEIGHT 768


struct Vertex
{
    Vector3f m_pos;
    Vector2f m_tex;

    Vertex() {}

    Vertex(Vector3f pos, Vector2f tex)
    {
        m_pos = pos;
        m_tex = tex;
    }
};


GLuint VBO;
GLuint IBO;


CTexture* pTexture = NULL;


GLuint CInitGL::gScaleLocation;
CInitGL *CInitGL::instance = 0;

GLuint CShaders::gWorldLocation;
GLuint CInitGL::VBO;
GLuint CInitGL::IBO;
GLuint CInitGL::gWVPLocation;


Ccamera* pGameCamera = NULL;


float CInitGL::Scale = 0.0f;

void CInitGL::setInstance(CInitGL *framework) {
	instance = framework;
}


void CInitGL::StartInitGL(int argc, char** argv)
    {
        glutInit(&argc,argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
      glutInitWindowSize (WINDOW_WIDTH, WINDOW_HEIGHT);
      glutInitWindowPosition(100,100);
      glutCreateWindow("Sector7_0.5.7");
      glutGameModeString("1920x1200@32");
      glutEnterGameMode();

      InitGlutCallbacks();

      pGameCamera = new Ccamera(WINDOW_WIDTH, WINDOW_HEIGHT);

      GLenum res = glewInit();
    if (res != GLEW_OK) {
      fprintf(stderr, "Error: '%s'\n", glewGetErrorString(res));

    }

        glClearColor(0.0f,0.0f,0.0f,0.0f);
        glFrontFace(GL_CW);
        glCullFace(GL_BACK);
        glEnable(GL_CULL_FACE);

        CreateVertexBuffer();
        CreateIndexBuffer();

        CompileShaders();

pTexture = new CTexture(GL_TEXTURE_2D, "test.png");

    if (!pTexture->Load()) {
       // return true;
    }

        glutMainLoop();

    }

void CInitGL::RenderScene()
    {
       pGameCamera->OnRender();

    glClear(GL_COLOR_BUFFER_BIT);

    static float Scale = 0.0f;

    Scale += 0.1f;

    CPipeline p;
    p.Rotate(0.0f, Scale, 0.0f);
    p.WorldPos(0.0f, 0.0f, 3.0f);
    p.SetCamera(pGameCamera->GetPos(), pGameCamera->GetTarget(), pGameCamera->GetUp());
    p.SetPerspectiveProj(60.0f, WINDOW_WIDTH, WINDOW_HEIGHT, 1.0f, 100.0f);

    glUniformMatrix4fv(gWVPLocation, 1, GL_TRUE, (const GLfloat*)p.GetTrans());

    glEnableVertexAttribArray(0);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (const GLvoid*)12);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);

        pTexture->Bind(GL_TEXTURE0);

    glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, 0);

    glDisableVertexAttribArray(0);
    glDisableVertexAttribArray(1);

    glutSwapBuffers();
}


void CInitGL::SpecialKeyboardCB(int Key, int x, int y)
    {
    pGameCamera->OnKeyboard(Key);
    }


 void CInitGL::KeyboardCB(unsigned char Key, int x, int y)
    {
    switch (Key) {
        case 'q':
            exit(0);
            break;
        }


    }

 void CInitGL::PassiveMouseCB(int x, int y)
    {
    pGameCamera->OnMouse(x, y);
    }

void CInitGL::InitGlutCallbacks()
    {
        glutDisplayFunc(RenderScene);
        glutIdleFunc(RenderScene);
        glutSpecialFunc(SpecialKeyboardCB);
        glutPassiveMotionFunc(PassiveMouseCB);
        glutKeyboardFunc(KeyboardCB);
    }

void CInitGL::CreateVertexBuffer()
    {
        Vertex Vertices[4] = { Vertex(Vector3f(-1.0f, -1.0f, 0.5773f), Vector2f(0.0f, 0.0f)),
                           Vertex(Vector3f(0.0f, -1.0f, -1.15475), Vector2f(0.5f, 0.0f)),
                           Vertex(Vector3f(1.0f, -1.0f, 0.5773f),  Vector2f(1.0f, 0.0f)),
                           Vertex(Vector3f(0.0f, 1.0f, 0.0f),      Vector2f(0.5f, 1.0f)) };

 	glGenBuffers(1, &VBO);
	glBindBuffer(GL_ARRAY_BUFFER, VBO);
	glBufferData(GL_ARRAY_BUFFER, sizeof(Vertices), Vertices, GL_STATIC_DRAW);

    }


void CInitGL::CreateIndexBuffer()
    {
      unsigned int Indices[] = {
                                    0, 3, 1,
                                    1, 3, 2,
                                    2, 3, 0,
                                    0, 2, 1
                                            };
        glGenBuffers(1, &IBO);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Indices), Indices, GL_STATIC_DRAW);
    }
